package service.Impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import handler.FinanceChainHandler;
import org.apache.commons.codec.digest.DigestUtils;
import pojo.SaveToBcResult;
import service.GeneralDataService;

import java.util.Arrays;
import java.util.Date;
import java.util.stream.Collectors;


/**
 * @Author ：zbzbzzz
 * @Date ：Created in 18:41 2020/1/1
 * @Description：_____
 * @Modified By：
 * @Version: X.X
 */
public class GeneralDataServiceImpl implements GeneralDataService {
    /**
     * 功能描述:财务数据添加hashcode,fieldorder,企业id并转进行上链操作
     *
     * @param orgid    企业id
     * @param dataname 表名
     * @param datakey  表的关键字
     * @param object   表
     * @return java.lang.String
     * @author zbzbzzz
     * @date 2020/1/1 18:55
     */
    @Override
    public SaveToBcResult saveToBc(String orgid, String dataname, String datakey, Object object) {

        try {
            // tmpData一开始用来保持jsonObject中的数据顺序不改变，后面用来保证生成hashcode不加上fieldorder
            String tmpData = JSON.toJSONString(object);
            JSONObject jsonObject = JSONObject.parseObject(tmpData, Feature.OrderedField);
            jsonObject.put("orgid", orgid);
            tmpData = jsonObject.toJSONString();

            // 获取字段Set后转成List防止生成的Set是浅拷贝后面添加的字段也会加进去
            Object[] list = jsonObject.keySet().toArray();
            jsonObject.put("fieldorder", list);

            // 生成hashcode
            String hash = DigestUtils.sha256Hex(tmpData);
            jsonObject.put("hashcode", hash);

            JSONObject result = JSONObject.parseObject(FinanceChainHandler.getInstance().saveGenericRecord(orgid, dataname, datakey, jsonObject.toJSONString()), Feature.OrderedField);
            SaveToBcResult saveToBcResult = new SaveToBcResult();
            saveToBcResult.setDate(new Date());

            // 判断上链是否成功,如果失败返回失败信息
            if(result.getIntValue("error")!=0){
                throw new RuntimeException("上链失败信息:"+result.getString("msg"));
            }
            saveToBcResult.setTransactionid(result.getJSONObject("data").getString("transactionid"));
            saveToBcResult.setFieldorder(Arrays.stream(list).map(Object :: toString).collect(Collectors.joining(",")));
            saveToBcResult.setHashcode(hash);

            return saveToBcResult;
        }
        catch (RuntimeException r)
        {
            r.printStackTrace();
            throw r;
        }
        catch (Exception e) {
            e.printStackTrace();
           throw new RuntimeException("普通数据上链出错");
        }
    }


    /**
     * 功能描述:接收数据的jsonString,分两步进行校验，
     *
     * @param orgid    企业id
     * @param dataname 表名
     * @param datakey  表的关键字
     * @param object   需要校验的数据
     * @return boolean
     * @author zbzbzzz
     * @date 2020/1/1 18:56
     */
    @Override
    public  boolean validateHash(String orgid, String dataname, String datakey, Object object) {
        try {
            JSONObject jsonObject = (JSONObject) JSONObject.toJSON(object);

            String hashcode = jsonObject.getString("hashcode");
            JSONArray jsonArray = (JSONArray) jsonObject.get("fieldorder");

            // 设置JSONObject的顺序不变
            JSONObject jsonbean = new JSONObject(true);
            for (Object filed : jsonArray) {
                jsonbean.put(filed.toString(), jsonObject.get(filed));
            }

            // 第一步生成本身数据的hash值并做校验
            String selfHash = DigestUtils.sha256Hex(jsonbean.toJSONString());
            if (hashcode.equals(selfHash)) {
                // 通过第一步后进行第二步对比链上hash
                JSONObject linkData = JSONObject.parseObject(FinanceChainHandler.getInstance().getGenericRecord(orgid, dataname, datakey), Feature.OrderedField);
                if(linkData.getIntValue("error")!=0)
                {
                    throw new RuntimeException("上链失败信息:"+linkData.getString("msg"));
                }
                if (hashcode.equals(linkData.getJSONObject("data").getString("hashcode"))) {
                    return true;
                }
            }

            return false;
        }
        catch (NullPointerException n)
        {
            // 校验数据中缺少字段返回false，校验失败
            n.printStackTrace();
            return false;
        }
        catch (RuntimeException r)
        {
            r.printStackTrace();
            return false;
        }
        catch (Exception e) {
            e.printStackTrace();
           throw new RuntimeException("普通数据校验出错");
        }
    }
}
